/**
 * Copyright (c) 2018-2028.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sailmi.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sailmi.common.constant.TokenConstant;
import com.sailmi.common.tool.api.R;
import com.sailmi.common.tool.utils.DigestUtil;
import com.sailmi.common.model.auth.AuthInfo;
import com.sailmi.common.model.auth.AuthUser;
import io.jsonwebtoken.Claims;
import com.sailmi.common.tool.utils.StringUtil;
import com.sailmi.gateway.props.AuthProperties;
import com.sailmi.gateway.provider.AuthProvider;
import com.sailmi.gateway.provider.ResponseProvider;
import com.sailmi.gateway.utils.JwtUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.server.Session;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 鉴权认证
 *
 * @author yzh
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {
	@Autowired
	private AuthProperties authProperties;
	@Autowired
	private ObjectMapper objectMapper;

	//用于记录用户登录的token,以判断用户是否在多终端登录
	private static final ConcurrentHashMap<String,String> authMap = new ConcurrentHashMap();

	//用于记录用户的每一次访问时间，以做为异常退出后移除的依据
	private static final ConcurrentHashMap<String,Map<String,Long>> visitMap = new ConcurrentHashMap<>();

	//用于记录用户登录的token,以判断用户是否在多终端登录
	private static final ConcurrentHashMap<String,String> kickMap = new ConcurrentHashMap();

	private final static Logger log = LoggerFactory.getLogger(AuthFilter.class);

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		String path = exchange.getRequest().getURI().getPath();
		if (isSkip(path)) {
			return chain.filter(exchange);
		}
		log.info("request path:{} uri:{} headers{}:",path,exchange.getRequest().getURI(),exchange.getRequest().getHeaders());
		ServerHttpResponse resp = exchange.getResponse();

		String headerToken = exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);
		String paramToken = exchange.getRequest().getQueryParams().getFirst(AuthProvider.AUTH_KEY);
		//debug by yzh
		log.debug("auth key is:"+AuthProvider.AUTH_KEY);
		log.info("headerToken:"+headerToken);
		log.info("paramToken:"+paramToken);
		//end
		if (StringUtil.isAllBlank(headerToken, paramToken)) {
			log.info("paramToken and headerToken all is null!");
			return unAuth(resp, "缺失令牌,鉴权失败");
		}
		String auth = StringUtil.isBlank(headerToken) ? paramToken : headerToken;
		String token = JwtUtil.getToken(auth);
		log.info("client token:"+token);
		Claims claims = JwtUtil.parseJWT(token);
		if (claims == null) {
			log.info("claims is null,阻止页面请求！");
			return unAuth(resp, "请求未授权");
		}else{
			String userId=String.valueOf(claims.get("user_id"));
			if(Objects.isNull(userId)||userId.equals("")||userId.equals("null")){
				try{
					userId = exchange.getRequest().getCookies().getFirst("userId").getValue();
					log.debug("parse cookie userId  :userId:"+userId);
				}catch(Exception ex){
					log.error("userId in cookie is null {}",ex.getMessage());
				}
			}
		}
		return chain.filter(exchange);
	}
	private String getVisitKey(String path,String userId,String token){
		return path;
	}

	private String getAuthKey(String path,String userId){
		//String tokenDigest = DigestUtil.md5DigestAsHex(token.getBytes());
		String authKey=path+"#"+userId;
		return authKey;
	}
	//检查当前链接是否需要跳过权限检查
	private boolean isSkip(String path) {
		List<String> skipList = AuthProvider.getDefaultSkipUrl();
		if(Objects.nonNull(authProperties)){
			skipList.addAll(authProperties.getSkipUrl());
		}else{
			log.info("Skip Url From Setting Service Is NULL!");
		}
		return skipList.stream().map(url -> url.replace(AuthProvider.TARGET, AuthProvider.REPLACEMENT)).anyMatch(path::contains);
	}

	private Mono<Void> unAuth(ServerHttpResponse resp, String msg) {
		resp.setStatusCode(HttpStatus.UNAUTHORIZED);
		resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
		String result = "";
		try {
			result = objectMapper.writeValueAsString(ResponseProvider.unAuth(msg));
		} catch (JsonProcessingException e) {
			log.error(e.getMessage(), e);
		}
		DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
		return resp.writeWith(Flux.just(buffer));
	}

	@Override
	public int getOrder() {
		return -100;
	}

}
